﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace SoundSynth.AudioPatch
{
    [DataContract]
    public class AudioPatch:IInstrument
    {
        
        [DataMember]
        public List<AudioPatchPart> Parts { get; set; }

        public AudioPatch()
        {
            Parts = new List<AudioPatchPart>();
        }





        private AudioPatchPart GetPart(float frequencyRatio)
        {
            var freq = Math.Round(frequencyRatio * Note.Frequency["C"], 1);// *(this.Frequency / 440); ;
            AudioPatchPart thePart = Parts.FirstOrDefault(i => i.MinFrequency <= ((freq)) && i.MaxFrequency >= ((freq)));
            if (thePart == null) thePart = Parts.First();
            return thePart;
        }

        public void SaveToFile(Stream stream)
        {
            System.Runtime.Serialization.Json.DataContractJsonSerializer ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(AudioPatch), new List<Type> { typeof(AudioPatch),typeof(AudioPatchPart),typeof(byte[]) });
            ser.WriteObject(stream, this);
        }

        public static AudioPatch ReadFromFile(Stream stream)
        {
            System.Runtime.Serialization.Json.DataContractJsonSerializer ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(AudioPatch), new List<Type> { typeof(AudioPatch),typeof(AudioPatchPart),typeof(byte[]) });
            return (AudioPatch)ser.ReadObject(stream);
        }

        #region IInstrument;
        [DataMember]
        public bool IsPlugged { get;set; }
        public float GetCorrectFrequencyRatio(float ratio)
        {
            var thePart = GetPart(ratio);
            var theFrequency = ratio * Note.Frequency["C"];
            var r = theFrequency / thePart.BaseFrequency;
            return (float)r;
        }



        public System.IO.MemoryStream GetDataStream(float fratio)
        {
            var thePart = GetPart(fratio);
            return thePart.GetDataStream();
        }

        public System.IO.MemoryStream GetDataStream(int sampleIndex)
        {
            return Parts[sampleIndex].GetDataStream();
        }

        public bool ReloadNeeded(float newFrequencyRatio, float oldFrequencyRatio)
        {
            return GetPart(newFrequencyRatio) != GetPart(oldFrequencyRatio);
        }
        #endregion
    }

    [DataContract]
    public class AudioPatchPart
    {
        [DataMember]
        public double SampleRate { get; set; }
        [DataMember]
        public float BaseFrequency { get; set; }
        [DataMember]
        public float MinFrequency { get; set; }
        [DataMember]
        public float MaxFrequency { get; set; }
        [IgnoreDataMember]
        public byte[] WaveData { get; set; }
        [DataMember]
        string Serialized
        {
            get { return Convert.ToBase64String(WaveData); }
            set { WaveData = Convert.FromBase64String(value); }
        }

        public System.IO.MemoryStream GetDataStream()
        {
            System.IO.MemoryStream stream = new MemoryStream();
            CreateHeader(stream, WaveData, (uint)SampleRate);
            System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream);
            writer.Write(WaveData);
            stream.Seek(0, SeekOrigin.Begin);
           
            return stream;
        }

        public void CreateHeader(Stream stream, byte[] WaveData, uint SampleRate)
        {
            var theData = WaveData;
            var sampleRate = SampleRate;
            var Channels = 2;
            var dataCount = theData;
            var sampleCount = dataCount.Length / (Channels * 2);
            System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream);
            int RIFF = 0x46464952;
            int WAVE = 0x45564157;
            int formatChunkSize = 16;
            int headerSize = 8;
            int format = 0x20746D66;
            short formatType = 1;
            short tracks = (short)Channels;
            short bitsPerSample = 16;
            short frameSize = (short)(tracks * ((bitsPerSample + 7) / 8));
            int bytesPerSecond = (int)(sampleRate * frameSize);
            int waveSize = 4;
            int data = 0x61746164;
            int samples = (int)sampleCount;
            int dataChunkSize = samples * frameSize;
            int fileSize = waveSize + headerSize + formatChunkSize + headerSize + dataChunkSize;
            writer.Write(RIFF);
            writer.Write(fileSize);
            writer.Write(WAVE);
            writer.Write(format);
            writer.Write(formatChunkSize);
            writer.Write(formatType);
            writer.Write(tracks);
            writer.Write((int)sampleRate);
            writer.Write(bytesPerSecond);
            writer.Write(frameSize);
            writer.Write(bitsPerSample);
            writer.Write(data);
            writer.Write(dataChunkSize);
        }
    }
}
